;;; packages.el --- Clojure Layer packages File for Spacemacs  -*- lexical-binding: nil; -*-
;;
;; Copyright (c) 2012-2025 Sylvain Benner & Contributors
;;
;; Author: Sylvain Benner <sylvain.benner@gmail.com>
;; URL: https://github.com/syl20bnr/spacemacs
;;
;; This file is not part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.


(defconst clojure-packages
  '(
    cider
    cider-eval-sexp-fu
    (clj-refactor :toggle clojure-enable-clj-refactor)
    (helm-cider :toggle (configuration-layer/layer-used-p 'helm))
    clojure-mode
    (clojure-snippets :toggle (configuration-layer/layer-used-p 'auto-completion))
    company
    eldoc
    evil-cleverparens
    flycheck
    (flycheck-clojure :requires flycheck
                      :toggle (memq 'squiggly (if (listp clojure-enable-linters)
                                                  clojure-enable-linters
                                                (list clojure-enable-linters))))
    (flycheck-clj-kondo :requires flycheck
                        :toggle (memq 'clj-kondo (if (listp clojure-enable-linters)
                                                     clojure-enable-linters
                                                   (list clojure-enable-linters))))
    (flycheck-joker :requires flycheck
                    :toggle (memq 'joker (if (listp clojure-enable-linters)
                                             clojure-enable-linters
                                           (list clojure-enable-linters))))
    ggtags
    (kaocha-runner :toggle clojure-enable-kaocha-runner)
    org
    popwin
    (sayid :toggle clojure-enable-sayid)
    smartparens
    subword))


(defun clojure/init-cider ()
  (use-package cider
    :defer t
    :init
    (spacemacs/register-repl 'cider 'cider-jack-in "cider")
    (setq cider-stacktrace-default-filters '(tooling dup)
          cider-repl-pop-to-buffer-on-connect nil
          cider-prompt-save-file-on-load nil
          cider-repl-use-clojure-font-lock t
          cider-repl-history-file (concat spacemacs-cache-directory "cider-repl-history"))
    (add-hook 'clojure-mode-hook 'cider-mode)

    (dolist (x '(spacemacs-jump-handlers-clojure-mode
                 spacemacs-jump-handlers-clojurec-mode
                 spacemacs-jump-handlers-clojurescript-mode
                 spacemacs-jump-handlers-clojurex-mode
                 spacemacs-jump-handlers-cider-repl-mode))
      (add-to-list x '(spacemacs/clj-find-var :async t)))

    ;; TODO: having this work for cider-macroexpansion-mode would be nice,
    ;;       but the problem is that it uses clojure-mode as its major-mode
    (let ((cider--key-binding-prefixes
           '(("m=e" . "edn")
             ("md" . "debug")
             ("mdv" . "inspect values")
             ("me" . "evaluation")
             ("men" . "namespace")
             ("mep" . "pretty print")
             ("mm" . "manage repls")
             ("mml" . "link session")
             ("mmS" . "sibling sessions")
             ("mmq" . "quit/restart")
             ("mp" . "profile")
             ("ms" . "send to repl")
             ("msc" . "connect external repl")
             ("msj" . "jack-in")
             ("msq" . "quit/restart repl")
             ("mt" . "test")))
          (cider--key-binding-non-lsp-prefixes
           '(("m=" . "format")
             ("mg" . "goto") ;; no lsp
             ("mh" . "documentation")
             ("mT" . "toggle"))))
      (spacemacs|forall-clojure-modes m
        (mapc (lambda (x) (spacemacs/declare-prefix-for-mode
                            m (car x) (cdr x)))
              cider--key-binding-prefixes)
        (unless (eq clojure-backend 'lsp)
          (mapc (lambda (x) (spacemacs/declare-prefix-for-mode
                              m (car x) (cdr x)))
                cider--key-binding-non-lsp-prefixes)
          (spacemacs/set-leader-keys-for-major-mode m
            "hh" 'cider-doc
            "=r" 'cider-format-region
            "ge" 'cider-jump-to-compilation-error
            "gr" 'cider-find-resource
            "gs" 'cider-browse-spec
            "gS" 'cider-browse-spec-all))
        (spacemacs/set-leader-keys-for-major-mode m
          ;; shortcuts
          "'"  'sesman-start
          ;; help / documentation
          "ha" 'cider-apropos
          "hc" 'cider-cheatsheet
          "hd" 'cider-clojuredocs
          "hj" 'cider-javadoc
          "hn" 'cider-browse-ns
          "hN" 'cider-browse-ns-all
          "hs" 'cider-browse-spec
          "hS" 'cider-browse-spec-all
          ;; evaluate in source code buffer
          "e;" 'cider-eval-defun-to-comment
          "e$" 'spacemacs/cider-eval-sexp-end-of-line
          "e(" 'cider-eval-list-at-point
          "eb" 'cider-eval-buffer
          "ee" 'cider-eval-last-sexp
          "ef" 'cider-eval-defun-at-point
          "ei" 'cider-interrupt
          "el" 'spacemacs/cider-eval-sexp-end-of-line
          "em" 'cider-macroexpand-1
          "eM" 'cider-macroexpand-all
          "ena" 'cider-ns-reload-all
          "enn" 'cider-eval-ns-form
          "enr" 'cider-ns-refresh
          "enl" 'cider-ns-reload  ;; SPC u for cider-ns-reload-all
          "ep;" 'cider-pprint-eval-defun-to-comment
          "ep:" 'cider-pprint-eval-last-sexp-to-comment
          "epf" 'cider-pprint-eval-defun-at-point
          "epe" 'cider-pprint-eval-last-sexp
          "er" 'cider-eval-region
          "eu" 'cider-undef
          "ev" 'cider-eval-sexp-at-point
          "eV" 'cider-eval-sexp-up-to-point
          "ew" 'cider-eval-last-sexp-and-replace
          ;; format code style
          "==" 'cider-format-buffer
          "=eb" 'cider-format-edn-buffer
          "=ee" 'cider-format-edn-last-sexp
          "=er" 'cider-format-edn-region
          "=f" 'cider-format-defun
          ;; goto
          "gb" 'cider-pop-back
          "gc" 'cider-classpath
          "gg" 'spacemacs/clj-find-var
          "gn" 'cider-find-ns
          ;; manage cider connections / sesman
          "mb" 'sesman-browser
          "mi" 'sesman-info
          "mg" 'sesman-goto
          "mlb" 'sesman-link-with-buffer
          "mld" 'sesman-link-with-directory
          "mlu" 'sesman-unlink
          "mqq" 'sesman-quit
          "mqr" 'sesman-restart
          "mlp" 'sesman-link-with-project
          "mSj" 'cider-connect-sibling-clj
          "mSs" 'cider-connect-sibling-cljs
          "ms" 'sesman-start
          ;; send code - spacemacs convention
          "sa" (if (eq m 'cider-repl-mode)
                   'cider-switch-to-last-clojure-buffer
                 'cider-switch-to-repl-buffer)
          "sb" 'cider-load-buffer
          "sB" 'spacemacs/cider-send-buffer-in-repl-and-focus
          "scj" 'cider-connect-clj
          "scm" 'cider-connect-clj&cljs
          "scs" 'cider-connect-cljs
          "se" 'spacemacs/cider-send-last-sexp-to-repl
          "sE" 'spacemacs/cider-send-last-sexp-to-repl-focus
          "sf" 'spacemacs/cider-send-function-to-repl
          "sF" 'spacemacs/cider-send-function-to-repl-focus
          "si" 'sesman-start
          "sjj" 'cider-jack-in-clj
          "sjm" 'cider-jack-in-clj&cljs
          "sjs" 'cider-jack-in-cljs
          "sl" 'spacemacs/cider-find-and-clear-repl-buffer
          "sL" 'cider-find-and-clear-repl-output
          "sn" 'spacemacs/cider-send-ns-form-to-repl
          "sN" 'spacemacs/cider-send-ns-form-to-repl-focus
          "so" 'cider-repl-switch-to-other
          "sqq" 'cider-quit
          "sqr" 'cider-restart
          "sqn" 'cider-ns-reload
          "sqN" 'cider-ns-reload-all
          "sr" 'spacemacs/cider-send-region-to-repl
          "sR" 'spacemacs/cider-send-region-to-repl-focus
          "su" 'cider-repl-require-repl-utils
          ;; toggle options
          "Te" 'cider-enlighten-mode
          "Tf" 'spacemacs/cider-toggle-repl-font-locking
          "Tp" 'spacemacs/cider-toggle-repl-pretty-printing
          "Tt" 'cider-auto-test-mode
          ;; cider-tests
          "ta" 'spacemacs/cider-test-run-all-tests
          "tb" 'cider-test-show-report
          "tl" 'spacemacs/cider-test-run-loaded-tests
          "tn" 'spacemacs/cider-test-run-ns-tests
          "tp" 'spacemacs/cider-test-run-project-tests
          "tr" 'spacemacs/cider-test-rerun-failed-tests
          "tt" 'spacemacs/cider-test-run-focused-test
          ;; cider-debug and inspect
          "db" 'cider-debug-defun-at-point
          "de" 'spacemacs/cider-display-error-buffer
          "dve" 'cider-inspect-last-sexp
          "dvf" 'cider-inspect-defun-at-point
          "dvi" 'cider-inspect
          "dvl" 'cider-inspect-last-result
          "dvv" 'cider-inspect-expr
          ;; profile
          "p+" 'cider-profile-samples
          "pc" 'cider-profile-clear
          "pn" 'cider-profile-ns-toggle
          "ps" 'cider-profile-var-summary
          "pS" 'cider-profile-summary
          "pt" 'cider-profile-toggle
          "pv" 'cider-profile-var-profiled-p)))

    ;; cider-repl-mode only
    (spacemacs/set-leader-keys-for-major-mode 'cider-repl-mode
      "," 'cider-repl-handle-shortcut)
    (spacemacs/set-leader-keys-for-major-mode 'cider-clojure-interaction-mode
      "epl" 'cider-eval-print-last-sexp)
    :config
    ;; add support for golden-ratio
    (with-eval-after-load 'golden-ratio
      (add-to-list 'golden-ratio-extra-commands 'cider-popup-buffer-quit-function))
    ;; add support for evil
    (evil-set-initial-state 'cider-stacktrace-mode 'motion)
    (evil-set-initial-state 'cider-popup-buffer-mode 'motion)
    (add-hook 'cider--debug-mode-hook 'spacemacs/cider-debug-setup)

    (evilified-state-evilify-map cider-stacktrace-mode-map
      :mode cider-stacktrace-mode
      :bindings
      (kbd "C-j") 'cider-stacktrace-next-cause
      (kbd "C-k") 'cider-stacktrace-previous-cause
      (kbd "TAB") 'cider-stacktrace-cycle-current-cause
      (kbd "0")   'cider-stacktrace-cycle-all-causes
      (kbd "1")   'cider-stacktrace-cycle-cause-1
      (kbd "2")   'cider-stacktrace-cycle-cause-2
      (kbd "3")   'cider-stacktrace-cycle-cause-3
      (kbd "4")   'cider-stacktrace-cycle-cause-4
      (kbd "5")   'cider-stacktrace-cycle-cause-5
      (kbd "a")   'cider-stacktrace-toggle-all
      (kbd "c")   'cider-stacktrace-toggle-clj
      (kbd "d")   'cider-stacktrace-toggle-duplicates
      (kbd "J")   'cider-stacktrace-toggle-java
      (kbd "r")   'cider-stacktrace-toggle-repl
      (kbd "T")   'cider-stacktrace-toggle-tooling)

    ;; open cider-doc directly and close it with q
    (setq cider-prompt-for-symbol t)

    (evilified-state-evilify-map cider-docview-mode-map
      :mode cider-docview-mode
      :bindings
      (kbd "q") 'cider-popup-buffer-quit)

    (evilified-state-evilify-map cider-inspector-mode-map
      :mode cider-inspector-mode
      :bindings
      (kbd "L") 'cider-inspector-pop
      (kbd "n") 'cider-inspector-next-page
      (kbd "N") 'cider-inspector-prev-page
      (kbd "p") 'cider-inspector-prev-page
      (kbd "r") 'cider-inspector-refresh)

    (evilified-state-evilify-map cider-test-report-mode-map
      :mode cider-test-report-mode
      :bindings
      (kbd "C-j") 'cider-test-next-result
      (kbd "C-k") 'cider-test-previous-result
      (kbd "RET") 'cider-test-jump
      (kbd "d")   'cider-test-ediff
      (kbd "e")   'cider-test-stacktrace
      (kbd "q")   'cider-popup-buffer-quit
      (kbd "r")   'cider-test-rerun-tests
      (kbd "t")   'cider-test-run-test
      (kbd "T")   'cider-test-run-ns-tests)

    (evilified-state-evilify-map cider-repl-history-mode-map
      :mode cider-repl-history-mode
      :bindings
      "j" 'cider-repl-history-forward
      "k" 'cider-repl-history-previous
      "s" (cond ((featurep 'helm-swoop) 'helm-swoop)
                ((featurep 'swiper) 'swiper)
                (t 'cider-repl-history-occur))
      "r" 'cider-repl-history-update)

    (spacemacs/set-leader-keys-for-major-mode 'cider-repl-history-mode
      "s" 'cider-repl-history-save)

    (evil-define-key 'normal cider-repl-mode-map
      (kbd "C-j") 'cider-repl-next-input
      (kbd "C-k") 'cider-repl-previous-input
      (kbd "RET") 'cider-repl-return)

    (if (package-installed-p 'company)
        (evil-define-key 'insert cider-repl-mode-map
          (kbd "C-j") 'spacemacs//clj-repl-wrap-c-j
          (kbd "C-k") 'spacemacs//clj-repl-wrap-c-k)
      (evil-define-key 'insert cider-repl-mode-map
        (kbd "C-j") 'cider-repl-next-input
        (kbd "C-k") 'cider-repl-previous-input))

    (evil-define-key 'insert cider-repl-mode-map
      (kbd "C-<return>") 'cider-repl-newline-and-indent
      (kbd "C-r") 'cider-repl-history)

    (when clojure-enable-fancify-symbols
      (clojure/fancify-symbols 'cider-repl-mode)
      (clojure/fancify-symbols 'cider-clojure-interaction-mode))

    (evil-add-command-properties 'cider-find-var :jump t)))

(defun clojure/init-cider-eval-sexp-fu ()
  (with-eval-after-load 'eval-sexp-fu
    (require 'cider-eval-sexp-fu)))

(defun clojure/init-clj-refactor ()
  (use-package clj-refactor
    :defer t
    :init
    (add-hook 'clojure-mode-hook 'clj-refactor-mode)
    :config
    (cljr-add-keybindings-with-prefix "C-c C-f")
    ;; Usually we do not set keybindings in :config, however this must be done
    ;; here because it reads the variable `cljr--all-helpers'. Since
    ;; `clj-refactor-mode' is added to the hook, this should trigger when a
    ;; clojure buffer is opened anyway, so there's no "keybinding delay".
    (spacemacs|forall-clojure-modes m
      (dolist (r cljr--all-helpers)
        (let* ((binding (car r))
               (func (cadr r)))
          (unless (string-prefix-p "hydra" (symbol-name func))
            (spacemacs/set-leader-keys-for-major-mode m
              (concat "r" binding) func)))))))

(defun clojure/init-helm-cider ()
  (use-package helm-cider
    :defer t
    :init
    (add-hook 'clojure-mode-hook 'helm-cider-mode)
    (setq sayid--key-binding-prefixes
          '(("mhc" . "helm-cider-cheatsheet")))
    (spacemacs|forall-clojure-modes m
      (mapc (lambda (x) (spacemacs/declare-prefix-for-mode
                          m (car x) (cdr x)))
            sayid--key-binding-prefixes)
      (spacemacs/set-leader-keys-for-major-mode m
        "hc" 'helm-cider-cheatsheet))))

(defun clojure/init-kaocha-runner ()
  (use-package kaocha-runner
    :defer t
    :init
    (setq kaocha--key-binding-prefixes
          '(("mtk" . "kaocha")))
    (spacemacs|forall-clojure-modes m
      (mapc (lambda (x) (spacemacs/declare-prefix-for-mode
                          m (car x) (cdr x)))
            kaocha--key-binding-prefixes)
      (spacemacs/set-leader-keys-for-major-mode m
        "tka" 'kaocha-runner-run-all-tests
        "tkt" 'kaocha-runner-run-test-at-point
        "tkn" 'kaocha-runner-run-tests
        "tkw" 'kaocha-runner-show-warnings
        "tkh" 'kaocha-runner-hide-windows))))

(defun clojure/init-clojure-mode ()
  (use-package clojure-mode
    :defer t
    :init
    (add-to-list 'auto-mode-alist '("\\.boot\\'" . clojure-mode))
    ;; This regexp matches shebang expressions like `#!/usr/bin/env boot'
    (add-to-list 'magic-mode-alist '("#!.*boot\\s-*$" . clojure-mode))
    (add-hook 'clojure-mode-hook #'spacemacs//clojure-setup-backend)
    ;; Define all the prefixes here, although most of them apply only to bindings in clj-refactor
    (let ((clj-refactor--key-binding-prefixes
           '(("mra" . "add")
             ("mrc" . "cycle/clean/convert")
             ("mrd" . "destructure")
             ("mre" . "extract/expand")
             ("mrf" . "find/function")
             ("mrh" . "hotload")
             ("mri" . "introduce/inline")
             ("mrm" . "move")
             ("mrp" . "project/promote")
             ("mrr" . "remove/rename/replace")
             ("mrs" . "show/sort/stop")
             ("mrt" . "thread")
             ("mru" . "unwind/update")))
          (clj-refactor--key-binding-non-lsp-prefixes
           '(("mr" . "refactor"))))
      (spacemacs|forall-clojure-modes m
        (mapc (lambda (x) (spacemacs/declare-prefix-for-mode
                            m (car x) (cdr x)))
              clj-refactor--key-binding-prefixes)
        (unless (eq clojure-backend 'lsp)
          (mapc (lambda (x) (spacemacs/declare-prefix-for-mode
                              m (car x) (cdr x)))
                clj-refactor--key-binding-non-lsp-prefixes))
        (spacemacs/set-leader-keys-for-major-mode m
          "=l" 'clojure-align
          "ran" 'clojure-insert-ns-form
          "raN" 'clojure-insert-ns-form-at-point
          "rci" 'clojure-cycle-if
          "rcp" 'clojure-cycle-privacy
          "rc#" 'clojure-convert-collection-to-set
          "rc'" 'clojure-convert-collection-to-quoted-list
          "rc(" 'clojure-convert-collection-to-list
          "rc[" 'clojure-convert-collection-to-vector
          "rc{" 'clojure-convert-collection-to-map
          "rc:" 'clojure-toggle-keyword-string
          "rsn" 'clojure-sort-ns
          "rtf" 'clojure-thread-first-all
          "rth" 'clojure-thread
          "rtl" 'clojure-thread-last-all
          "rua" 'clojure-unwind-all
          "ruw" 'clojure-unwind)
        (unless clojure-enable-clj-refactor
          (spacemacs/set-leader-keys-for-major-mode m
            "r?" 'spacemacs/clj-describe-missing-refactorings))))
    :config
    (when clojure-enable-fancify-symbols
      (spacemacs|forall-clojure-modes m
        (clojure/fancify-symbols m)))))

(defun clojure/post-init-eldoc ()
  (add-hook 'cider-mode-hook 'eldoc-mode)
  (add-hook 'cider-repl-mode-hook 'eldoc-mode)
  (add-hook 'cider-clojure-interaction-mode-hook 'eldoc-mode))

(defun clojure/pre-init-evil-cleverparens ()
  (spacemacs|use-package-add-hook evil-cleverparens
    :pre-init
    (spacemacs|forall-clojure-modes m
      (add-to-list 'evil-lisp-safe-structural-editing-modes m))))

(defun clojure/pre-init-popwin ()
  (spacemacs|use-package-add-hook popwin
    :post-config
    (push '("*cider-error*" :dedicated t :position bottom :stick t :noselect nil :height 0.4)
          popwin:special-display-config)
    (push '("*cider-doc*" :dedicated t :position bottom :stick t :noselect nil :height 0.4)
          popwin:special-display-config)))

(defun clojure/post-init-smartparens ()
  (add-hook 'cider-repl-mode-hook #'spacemacs//activate-smartparens)
  (with-eval-after-load 'smartparens
    (sp-local-pair 'clojure-mode "`" nil :actions nil)))

(defun clojure/post-init-subword ()
  (add-hook 'cider-mode-hook 'subword-mode))

(defun clojure/post-init-company ()
  (spacemacs|add-company-backends
    :backends company-capf
    :modes clojure-mode clojurec-mode clojurescript-mode clojurex-mode cider-clojure-interaction-mode cider-mode cider-repl-mode))

(defun clojure/post-init-ggtags ()
  (add-hook 'clojure-mode-local-vars-hook #'spacemacs/ggtags-mode-enable))

(defun clojure/init-clojure-snippets ()
  (use-package clojure-snippets
    :defer t))

(defun clojure/pre-init-org ()
  (spacemacs|use-package-add-hook org
    :post-config (add-to-list 'org-babel-load-languages '(clojure . t))
    (setq org-babel-clojure-backend 'cider)))

(defun clojure/init-sayid ()
  (use-package sayid
    :defer t
    :init
    (setq sayid--key-binding-prefixes
          '(("mdt" . "trace")))
    (spacemacs|forall-clojure-modes m
      (mapc (lambda (x) (spacemacs/declare-prefix-for-mode m
                          (car x) (cdr x)))
            sayid--key-binding-prefixes)
      (spacemacs/set-leader-keys-for-major-mode m
        ;;These keybindings mostly preserved from the default sayid bindings
        "d!" 'sayid-load-enable-clear
        "dE" 'sayid-eval-last-sexp ;in default sayid bindings this is lowercase e, but that was already used in clojure mode
        "dc" 'sayid-clear-log
        "df" 'sayid-query-form-at-point
        "dh" 'sayid-show-help
        "ds" 'sayid-show-traced
        "dS" 'sayid-show-traced-ns
        "dtb" 'sayid-trace-ns-in-file
        "dtd" 'sayid-trace-fn-disable
        "dtD" 'sayid-trace-disable-all
        "dte" 'sayid-trace-fn-enable
        "dtE" 'sayid-trace-enable-all
        "dtK" 'sayid-kill-all-traces
        "dtn" 'sayid-inner-trace-fn
        "dto" 'sayid-outer-trace-fn
        "dtp" 'sayid-trace-ns-by-pattern
        "dtr" 'sayid-remove-trace-fn
        "dty" 'sayid-trace-all-ns-in-dir
        "dV" 'sayid-set-view
        "dw" 'sayid-get-workspace
        "dx" 'sayid-reset-workspace))
    :config
    ;; If sayid-version is null the .elc file
    ;; is corrupted. Then force a reinstall and
    ;; reload the feature.
    (when (null sayid-version)
      (package-reinstall 'sayid)
      (unload-feature 'sayid)
      (require 'sayid)
      (setq cider-jack-in-lein-plugins (delete `("com.billpiel/sayid" nil) cider-jack-in-lein-plugins)))

    ;; Make it evil
    (evilified-state-evilify-map sayid-mode-map
      :mode sayid-mode
      :bindings
      (kbd "H") 'sayid-buf-show-help
      (kbd "n") 'sayid-buffer-nav-to-next
      (kbd "N") 'sayid-buffer-nav-to-prev
      (kbd "C-s v") 'sayid-toggle-view
      (kbd "C-s V") 'sayid-set-view
      (kbd "L") 'sayid-buf-back
      (kbd "e") 'sayid-gen-instance-expr) ;Originally this was bound to 'g', but I feel this is still mnemonic and doesn't overlap with evil
    (evilified-state-evilify-map sayid-pprint-mode-map
      :mode sayid-pprint-mode
      :bindings
      (kbd "h") 'sayid-pprint-buf-show-help
      (kbd "n") 'sayid-pprint-buf-next
      (kbd "N") 'sayid-pprint-buf-prev
      (kbd "l") 'sayid-pprint-buf-exit)
    (evilified-state-evilify-map sayid-traced-mode-map
      :mode sayid-traced-mode
      :bindings
      (kbd "l") 'sayid-show-traced
      (kbd "h") 'sayid-traced-buf-show-help)))


(defun clojure/post-init-flycheck ()
  ;; When user has chosen to use multiple linters.
  (when (> (safe-length clojure-enable-linters) 1)
    ;; If adding a linter, you must add to checkers-per-mode for each mode
    ;; it can support the mapping from the linter name in clojure-enable-linters
    ;; to the flycheck checker to use to add to flycheck.
    (let* ((checkers-per-mode '((clj . ((clj-kondo . clj-kondo-clj)
                                        (joker . clojure-joker)
                                        (squiggly . clojure-cider-eastwood)))
                                (cljc . ((clj-kondo . clj-kondo-cljc)
                                         (joker . clojure-joker)))
                                (cljs . ((clj-kondo . clj-kondo-cljs)
                                         (joker . clojurescript-joker)))
                                (edn . ((clj-kondo . clj-kondo-edn)
                                        (joker . edn-joker))))))
      ;; For each checker mode
      (dolist (mode-checkers checkers-per-mode)
        ;; We find the first checker in order from the user configured linters which
        ;; the mode supports and make it the primary-linter. All other linters after that
        ;; the mode support is made a next-linter. Finally, we extract the checkers of the
        ;; primary linter and the next linters.
        (let* ((checkers (cdr mode-checkers))
               (primary-linter (seq-find (lambda (l)
                                           (assq l checkers))
                                         clojure-enable-linters))
               (primary-checker (cdr (assq primary-linter checkers)))
               (next-linters (seq-filter (lambda (l)
                                           (and (not (eq l primary-linter))
                                                (assq l checkers)))
                                         clojure-enable-linters))
               (next-checkers (mapcar (lambda (l)
                                        (cdr (assq l checkers)))
                                      next-linters)))
          ;; Move primary checker to the front of flycheck lists of checkers so that
          ;; it is used as the primary checker, because flycheck picks the first one
          ;; it finds.
          (delq primary-checker flycheck-checkers)
          (push primary-checker flycheck-checkers)
          ;; For every checker, set their next checkers, starting with the primary
          ;; checker which has all others has a next-checker, and then the next
          ;; one has all the ones after it, and so on, until the last one which
          ;; has no next-checker to be added. This is because flycheck next-checkers
          ;; must be nested if we want more than two to run. It will pick the first
          ;; available next-checker from next-checkers and run that after. If we want
          ;; a checker after that one, it must also have next-checkers configured.
          (let ((checkers-to-add next-checkers))
            (dolist (checker (cons primary-checker next-checkers))
              (dolist (next-checker checkers-to-add)
                (flycheck-add-next-checker checker next-checker t))
              (setq checkers-to-add (cdr checkers-to-add))))))))
  (spacemacs|forall-clojure-modes m
    (spacemacs/enable-flycheck m)))

(defun clojure/init-flycheck-clojure ()
  (use-package flycheck-clojure
    :config
    (flycheck-clojure-setup)
    (with-eval-after-load 'cider
      (flycheck-clojure-inject-jack-in-dependencies))))

(defun clojure/init-flycheck-clj-kondo ()
  (use-package flycheck-clj-kondo))

(defun clojure/init-flycheck-joker ()
  (use-package flycheck-joker))
